﻿namespace TestTool.Tests.Common.Soap
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;

    public class SoapBuilder
    {
        private static Dictionary<Guid, XmlSerializer> _serializersCache = new Dictionary<Guid, XmlSerializer>();
        private static Uri _soapEnvelopeNS = new Uri("http://www.w3.org/2003/05/soap-envelope");
        protected static List<XmlSchema> _soapSchemas;

        static SoapBuilder()
        {
            Stream manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TestTool.Tests.Common.Discovery.Schemas.xml.xsd");
            XmlSchema schema = XmlSchema.Read(manifestResourceStream, null);
            manifestResourceStream.Close();
            manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TestTool.Tests.Common.Discovery.Schemas.soap-envelope.xsd");
            XmlSchema schema2 = XmlSchema.Read(manifestResourceStream, null);
            manifestResourceStream.Close();
            _soapSchemas = new List<XmlSchema> { schema, schema2 };
        }

        public static byte[] BuildMessage(object obj, Encoding encoding, ISoapHeaderBuilder header)
        {
            return BuildMessage(obj, encoding, header, null);
        }

        public static byte[] BuildMessage(object obj, Encoding encoding, ISoapHeaderBuilder header, XmlSerializerNamespaces namespaces)
        {
            XmlDocument document = new XmlDocument();
            MemoryStream inStream = new MemoryStream(ToXml(obj, encoding, null));
            document.Load(inStream);
            inStream.Close();
            Envelope envelope = new Envelope {
                Body = new Body()
            };
            envelope.Body.Any = new XmlElement[] { document.DocumentElement };
            if (header != null)
            {
                MemoryStream w = new MemoryStream();
                XmlTextWriter writer = new XmlTextWriter(w, Encoding.UTF8);
                writer.WriteStartDocument();
                writer.WriteStartElement("Header");
                header.WriteHeader(writer, obj);
                writer.WriteEndElement();
                writer.WriteEndDocument();
                writer.Flush();
                w.Seek(0L, SeekOrigin.Begin);
                XmlDocument document2 = new XmlDocument();
                document2.Load(w);
                w.Close();
                envelope.Header = new Header();
                List<XmlElement> list = new List<XmlElement>();
                foreach (XmlElement element in document2.DocumentElement.ChildNodes)
                {
                    list.Add(element);
                }
                envelope.Header.Any = list.ToArray();
            }
            return ToXml(envelope, encoding, namespaces);
        }

        protected static XmlAttributeOverrides GetAttributeOverrides<T>() where T: class
        {
            XmlElementAttribute attribute = new XmlElementAttribute {
                ElementName = GetTypeElementName(typeof(T)),
                Namespace = GetTypeNamespace(typeof(T))
            };
            XmlAttributes attributes = new XmlAttributes();
            attributes.XmlElements.Add(attribute);
            XmlAttributeOverrides overrides = new XmlAttributeOverrides();
            overrides.Add(typeof(Body<T>), "Element", attributes);
            return overrides;
        }

        protected static XmlSerializer GetDeserializer<T>(XmlAttributeOverrides overrides) where T: class
        {
            XmlSerializer serializer = null;
            Type type = typeof(T);
            if (_serializersCache.ContainsKey(type.GUID))
            {
                return _serializersCache[type.GUID];
            }
            serializer = new XmlSerializer(typeof(Envelope<T>), overrides);
            _serializersCache[type.GUID] = serializer;
            return serializer;
        }

        private static string GetTypeElementName(Type type)
        {
            string elementName = string.Empty;
            XmlRootAttribute[] customAttributes = type.GetCustomAttributes(typeof(XmlRootAttribute), false) as XmlRootAttribute[];
            if ((customAttributes != null) && (customAttributes.Length > 0))
            {
                elementName = customAttributes[0].ElementName;
            }
            if (string.IsNullOrEmpty(elementName))
            {
                return type.Name;
            }
            return elementName;
        }

        private static string GetTypeNamespace(Type type)
        {
            string str = string.Empty;
            XmlTypeAttribute[] customAttributes = type.GetCustomAttributes(typeof(XmlTypeAttribute), false) as XmlTypeAttribute[];
            if ((customAttributes != null) && (customAttributes.Length > 0))
            {
                str = customAttributes[0].Namespace;
            }
            return str;
        }

        protected static XmlReader GetValidatingReader(Stream stream, ICollection<XmlSchema> validateSchemas)
        {
            List<XmlSchema> list = new List<XmlSchema>();
            list.AddRange(_soapSchemas);
            if (validateSchemas != null)
            {
                list.AddRange(validateSchemas);
            }
            XmlReaderSettings settings = new XmlReaderSettings {
                ValidationType = ValidationType.Schema
            };
            settings.Schemas.XmlResolver = null;
            foreach (XmlSchema schema in list)
            {
                settings.Schemas.Add(schema);
            }
            return XmlReader.Create(stream, settings);
        }

        private static bool IsSoapFault(MemoryStream streamMessage)
        {
            return false;
        }

        private static Envelope<T> ParseEnvelope<T>(XmlReader reader) where T: class
        {
            return (Envelope<T>) GetDeserializer<T>(GetAttributeOverrides<T>()).Deserialize(reader);
        }

        public static SoapMessage<T> ParseMessage<T>(byte[] message, ICollection<XmlSchema> validateSchemas) where T: class
        {
            MemoryStream stream = new MemoryStream(message);
            XmlReader validatingReader = GetValidatingReader(stream, validateSchemas);
            Envelope<T> envelope = null;
            try
            {
                envelope = ParseEnvelope<T>(validatingReader);
            }
            finally
            {
                stream.Close();
            }
            List<XmlElement> header = new List<XmlElement>();
            if ((envelope.Header != null) && (envelope.Header.Any != null))
            {
                foreach (XmlElement element in envelope.Header.Any)
                {
                    header.Add(element);
                }
            }
            if (envelope.Body.Element != null)
            {
                return new SoapMessage<T>(header, envelope.Body.Element, message);
            }
            if ((envelope.Body.Any != null) && (envelope.Body.Any.Length == 1))
            {
                XmlElement element2 = envelope.Body.Any[0];
                Uri objB = !string.IsNullOrEmpty(element2.NamespaceURI) ? new Uri(element2.NamespaceURI) : null;
                if ((element2.LocalName == "Fault") && object.Equals(_soapEnvelopeNS, objB))
                {
                    throw new SoapFaultException(ParseMessage<Fault>(message, null));
                }
            }
            throw new UnxpectedElementException(string.Format("Element <{0}> not found", GetTypeElementName(typeof(T))), header);
        }

        protected static byte[] ToXml(object obj, Encoding encoding, XmlSerializerNamespaces namespaces)
        {
            MemoryStream w = new MemoryStream();
            XmlSerializer serializer = new XmlSerializer(obj.GetType());
            XmlTextWriter xmlWriter = new XmlTextWriter(w, encoding);
            if (namespaces != null)
            {
                serializer.Serialize(xmlWriter, obj, namespaces);
            }
            else
            {
                serializer.Serialize((XmlWriter) xmlWriter, obj);
            }
            xmlWriter.Flush();
            byte[] buffer = new byte[3];
            w.Seek(0L, SeekOrigin.Begin);
            w.Read(buffer, 0, 3);
            if (((buffer[0] != 0xef) || (buffer[1] != 0xbb)) || (buffer[2] != 0xbf))
            {
                w.Seek(0L, SeekOrigin.Begin);
            }
            byte[] buffer2 = new byte[w.Length - w.Position];
            w.Read(buffer2, 0, buffer2.Length);
            xmlWriter.Close();
            return buffer2;
        }

        public static string SoapEnvelopeUri
        {
            get
            {
                return _soapEnvelopeNS.ToString();
            }
        }
    }
}

